module sim12_dp (clk, reset, ibus, pc, memwrite, address, Ain, DataOut);
  input clk, reset;
  output memwrite;
  output [7:0] pc,  address;
  input [11:0] ibus, DataOut;
  output[11:0] Ain;
  wire [7:0] pc, pcnext, pcjump, pcplus;
  wire [2:0] alusrc, pipedAlusrc;
  wire [11:0] ibus, pipedibus,MDRout, Ain, Aout, pipedA, pipedMDR,Aluout, wd;
  wire jump, Awrite, MDRwrite, Acontrol, pipedAcontrol, ldisel, pipedLdisel;
  reg ldiR,  clear;
  wire [3:0]IR;

integer count, instruction, instrLDI, instrLD, instrST,instrSUBI, instrINC, instrJN, instrJMP; 


parameter NOP  = 4'b0000;
parameter LD   = 4'b0100;
parameter LDI  = 4'b0110;
parameter STI  = 4'b0111;
parameter SUBI = 4'b1101;
parameter ST   = 4'b0101;
parameter INC  = 4'b1100;
parameter JMP	 = 4'b0001;
parameter JN	  = 4'b0010;
parameter JZ		 = 4'b0011;
parameter HALT	= 4'b1111;

initial
begin
     ldiR=1'b0; 
end

always@(posedge clk) 
  begin
        if(!reset)                     // if reset line is low do the operation
          begin
                count=count+1;   // increase the count
          end
        else 
          begin                           // if reset line is high
                count=0;            // then reset the count to zero
        end
  end

//Fetch stage
//PC=PC+1
adder pcadd(
      .a(pc), 
      .b(8'b00000001), 
      .y(pcplus)
      );

  
piperegPC PC(
	   .clk(clk),
	   .in(pcnext),
	   .out(pc),
	   .reset(reset)
	   );

assign IR = ibus[11:8];	

always@(IR)  
begin
  if (IR == HALT) begin  //HALT instruction stops the simulation
    assign clear = 1'b1;            //cea pipelines
    $display ("Number of clock cycles taken= %d", count);
    $display ("Number of instruction executed = %d", instruction);
    $display ("Number of LDI instruction executed = %d", instrLDI);
    $display ("Number of LD instruction executed = %d", instrLD);
    $display ("Number of SUBI instruction executed = %d", instrSUBI);
    $display ("Number of ST instruction executed = %d", instrST);
    $display ("Number of INC instruction executed = %d", instrINC);
    $display ("Number of JN instruction executed = %d", instrJN);
    $display ("Number of JMP instruction executed = %d", instrJMP);
    $stop;                                 //stop the simulation
  end
else  
  begin
        if(!reset && IR != NOP)             // if reset line is low and instruction is NOP, do not count
        begin
                instruction=instruction+1;   // increase the instruction number
        end
        else if (reset) begin  
                instruction=0;
                instrLDI=0;
                instrLD=0;
                instrST=0;
                instrSUBI=0;
                instrINC=0;
                instrJN=0;
                instrJMP=0;
        end
        else begin                         // if NOP do not increase instruction count
                instruction = instruction;            
        end
        
        if (IR == LDI) begin  
                instrLDI=instrLDI+1;
        end
        else if (IR == LD) begin  
                instrLD=instrLD+1;
        end
        else if (IR == ST) begin  
                instrST=instrST+1;
        end
        else if (IR == INC) begin  
                instrINC=instrINC+1;
        end
        else if (IR == SUBI) begin  
                instrSUBI=instrSUBI+1;
        end
        else if (IR == JN) begin  
                instrJN=instrJN+1;
        end
        else if (IR == JMP) begin  
                instrJMP=instrJMP+1;
        end
        
  end 
end

assign ldisel = ldiR ? 1'b1 : 1'b0;
assign jump = ((IR==JMP) || (IR ==JN && Ain[11]== 1 ) || (IR ==JZ && Ain == 8'b0))  ? 1'b1 : 1'b0;
assign pcjump = ibus[7:0];
      
mux2X1_8 pcmux(
      .in1(pcplus), 
      .in2(pcjump), 
      .sel(jump),
      .out(pcnext)
       );


piperegIFID IFID(
	   .clk(clk),
    	.in1(ibus),
    	.in2(ldisel),
    	.out1(pipedibus),
    	.out2(pipedLdisel),
    	.reset(clear)
    	);

//Decode stage       
control c(pipedibus[11:8], alusrc, Acontrol, MDRwrite, Awrite, memwrite);

mux2X1_8 addr(
     .in1(pipedibus[7:0]), 
     .in2(MDRout[7:0]), 
     .sel(pipedLdisel),
     .out(address)
      );
  

	
regfile  MDR(
      .clk(clk),
      .we(MDRwrite), 
      .wd(DataOut), 
      .rd(MDRout)
      );
    
    
always@(pipedibus[11:8]) 
begin
assign ldiR = ((pipedibus[11:8]==LDI) || (pipedibus[11:8]==STI) ||(pipedibus[11:8]==SUBI))? 1'b1 : 1'b0;
end


piperegIDEX IDEX(
	   .clk(clk),
	   .in1(Aout),
	   .in2(DataOut),
	   .in3(alusrc),
	   .in4(Acontrol),
	   .out1(pipedA),
	   .out2(pipedMDR),
	   .out3(pipedAlusrc),
	   .out4(pipedAcontrol),
	   .reset(clear)
	   );

//Execute stage
//mux2X1_12 SrcAsel(
  //    .in1(pipedA),
    //  .in2(12'b000000000001),
     // .sel(pipedAlusrc[2]),
      //.out(SrcA)
      //);	

alu alu(
      .a(pipedA), 
      .b(pipedMDR),
      .alusrc(pipedAlusrc),
		  .result(Aluout)
		  );

mux2X1_12 Ainput(
      .in1(Aluout),
      .in2(MDRout),
      .sel(pipedAcontrol),
      .out(Ain)
      );	
      	      
regfile  A(
      .clk(clk),
      .we(Awrite),
      .wd(Ain), 
      .rd(Aout)
      );
	
endmodule
